/**
 * 
 */
package fr.loria.nancytag.tagger.lang.fr.maxent;

import java.util.StringTokenizer;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;

import vn.hus.nlp.utils.UTF8FileUtility;

import edu.stanford.nlp.ling.Sentence;
import edu.stanford.nlp.ling.TaggedWord;
import edu.stanford.nlp.tagger.maxent.MaxentTagger;
import fr.loria.led.fs.FeatureStructure;
import fr.loria.nancytag.tagger.AbstractTagger;

/**
 * @author LE HONG Phuong, phuonglh@gmail.com
 * <br>
 * Apr 9, 2009, 5:50:08 PM
 * <br>
 * The tagger for the French language using a maxent approach.
 * 
 */
public class FrenchMaxentTagger extends AbstractTagger {
	
	/**
	 * The maxent tagger for French
	 */
	private MaxentTagger tagger;
	
	/**
	 * The number of tagged sentences
	 */
	private static int nSents = 0;
	
	/**
	 * Initializes the tagger using the default model resource.
	 */
	public FrenchMaxentTagger() {
		super();
		tagger = FrenchMaxentTaggerProvider.getInstance();
	}
	
	/**
	 * Initialises the tagger given a list of resources.
	 * @param resources resource filenames.
	 */
	public FrenchMaxentTagger(String[] resources) {
		// this will call the init() method for initializing the 
		// tagger
		super(resources);
	}
	
	/**
	 * The French maxent tagger uses only a resource file which contains a pretrained tagger.
	 * The input array contains only one model file. 
	 * @see fr.loria.nancytag.tagger.lang.fr.maxent.AbstractTagger#init(java.lang.String[])
	 */
	@Override
	public void init(String[] filenames) {
		// the resource in use is the tagger model
		if (filenames == null || filenames.length != 1) {
			System.err.println("Tagger resource is invalid. ");
		}
		// init the tagger:
		tagger = FrenchMaxentTaggerProvider.getInstance(filenames[0]);
	}
	
	/* (non-Javadoc)
	 * @see fr.loria.nancytag.tagger.lang.fr.maxent.AbstractTagger#clean()
	 */
	@Override
	public void clean() {
		// TODO Auto-generated method stub

	}


	/* (non-Javadoc)
	 * @see fr.loria.nancytag.tagger.lang.fr.maxent.AbstractTagger#tag(java.lang.String)
	 */
	@Override
	public FeatureStructure tag(String sentence) {
		FeatureStructure sentFs = new FeatureStructure();
		sentFs.addAttValPair(FsConstants.SENTENCE, new FeatureStructure(sentence));
		sentFs.addAttValPair(FsConstants.ID, new FeatureStructure("s1"));
		Sentence<TaggedWord> taggedWords;
		try {
			// tag the sentence
			taggedWords = MaxentTagger.tagStringTokenized(sentence);
			int i = 0;
			for (TaggedWord taggedWord : taggedWords) {
				FeatureStructure wordFs = new FeatureStructure();
				// create the fs for this word
				wordFs.addAttValPair(FsConstants.ID, new FeatureStructure("t" + i));
				wordFs.addAttValPair(FsConstants.LOC, new FeatureStructure("" + i));
				wordFs.addAttValPair(FsConstants.LEMMA, new FeatureStructure(taggedWord.word()));
				wordFs.addAttValPair(FsConstants.WORD, new FeatureStructure(taggedWord.word()));
				wordFs.addAttValPair(FsConstants.CAT, new FeatureStructure(taggedWord.tag()));
				// the morph is empty
				wordFs.addAttValPair(FsConstants.MORPH, new FeatureStructure());
				
				// add the this word fs to the sentence fs
				sentFs.addAttValPair("" + i, wordFs);
				i++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return sentFs;
	}

	/**
	 * @return the tagger in use
	 */
	public MaxentTagger getTagger() {
		return tagger;
	}
	
	/**
	 * @author LE HONG Phuong, phuonglh@gmail.com
	 * <br>
	 * Jun 16, 2009, 12:19:19 PM
	 * <br>
	 */
	private interface FsConstants {
		static String SENTENCE = "sentence";
		static String ID = "id";
		static String LOC = "loc";
		static String LEMMA = "lemma";
		static String WORD = "word";
		static String MORPH = "morph";
		static String CAT = "cat";
	}

	/**
	 * Tokenizes a French string. This methods is used for preprocessing a string 
	 * to be tagged.
	 * @param s a string
	 * @return a tokenized string.
	 * @see #tagString(String)
	 */
	private String tokenizeString(String s) {
		StringTokenizer tokenizer = new StringTokenizer(s, " \"\'.,?:!()[]$€", true);
		StringBuffer buffer = new StringBuffer();
		while (tokenizer.hasMoreElements()) {
			String token = (String) tokenizer.nextElement(); 
			if (!token.equals("\'")) {
				buffer.append(" ");
			}
			buffer.append(token);
		}
		return buffer.toString().trim();
	}
	
	/* (non-Javadoc)
	 * @see fr.loria.nancytag.tagger.AbstractTagger#tagString(java.lang.String)
	 */
	@Override
	public String tagString(String toTag) {
		if (toTag.length() > 0) {
			nSents++;
			try {
				return MaxentTagger.tagString(tokenizeString(toTag));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * Tags a text file
	 * @param  fileInp input file
	 * @param fileOut output file
	 */
	public void tag(String fileInp, String fileOut) {
		UTF8FileUtility.createWriter(fileOut);
		String[] sentences = UTF8FileUtility.getLines(fileInp);
		for (String sent : sentences) {
			UTF8FileUtility.write(tagString(sent));
			UTF8FileUtility.write("\n");
		}
		UTF8FileUtility.closeWriter();
	}
	/**
	 * The main entry of the program.
	 * @param args
	 */
	public static void main(String[] args) {
		// create Options object
		Options options = new Options();
//		options.addOption("xo", false, "Use XML output");
		options.addOption("i", true, "Input file");
		options.addOption("o", true, "Output file");
	    // create the parser
	    CommandLineParser parser = new PosixParser();
	    HelpFormatter formatter = new HelpFormatter();
	    
	    try {
	        // parse the command line arguments
	        CommandLine line = parser.parse(options, args);
	        
	        String inputFile = null;
	        String outputFile = null;
	        // get the input file
	        if (line.hasOption("i")) {
	        	inputFile = line.getOptionValue("i");
	        } else {
        		System.err.println("You must provide an input French text file.");
        		formatter.printHelp("FrenchMaxentTagger", options);
        		System.exit(1);
	        }
	        // get the output file
	        if (line.hasOption("o")) {
	        	outputFile = line.getOptionValue("o");
	        } else {
        		System.err.println("You must provide an output file name.");
        		formatter.printHelp("FrenchMaxentTagger", options);
        		System.exit(1);
	        }
//	        if (line.hasOption("xo")) {
//	        	fr.loria.nancytag.tagger.lang.fr.maxent.Options.XML_OUTPUT = true;
//	        }
	        // create the tagger and tag the file
	        FrenchMaxentTagger tagger = new FrenchMaxentTagger();
	        System.out.println("The file is being tagged. Please wait...");
	        long beginTime = System.currentTimeMillis();
	        tagger.tag(inputFile, outputFile);
	        long endTime = System.currentTimeMillis();
	        System.out.println("Tagged " + nSents + " sentences in " + (endTime - beginTime) + " (ms)");
	    }  catch (ParseException exp) {
	        // oops, something went wrong
	        System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
	    	formatter.printHelp("FrenchMaxentTagger", options);
	    	System.exit(1);
	    }
	
	}
}
